import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import threading
import time
import json
import os
import sys
from datetime import datetime
from global_settings import debug_print


class LiquidCoolerUI:
    def __init__(self, root, serial_manager, protocol_parser, data_logger, config_manager):
        self.root = root
        self.root.title("液冷机通信控制软件")
        self.root.geometry("1400x800")

        # 设置窗口图标
        self.set_app_icon()

        # 设置中文字体
        self.setup_chinese_font()

        # 初始化组件
        self.serial_manager = serial_manager
        self.protocol_parser = protocol_parser
        self.data_logger = data_logger
        self.config_manager = config_manager

        # 加载配置
        self.config = self.config_manager.load_config()

        # 协议配置
        self.current_protocol = None
        self.protocols_dir = "protocol_configs"
        self.available_protocols = self.load_available_protocols()

        # 通信日志颜色配置
        self.tx_color = "#00FF00"  # 绿色
        self.rx_color = "#FFA500"  # 橙色
        self.timestamp_color = "#4DBEEE"

        # 初始化组件，传递日志回调函数
        self.serial_manager.log_callback = self.log_communication

        # 数据存储
        self.register_values = {}
        self.timestamps = []
        self.is_connected = False
        self.is_logging = False

        # 定时读取控制
        self.is_reading_ro = False  # 只读寄存器读取状态
        self.is_reading_rw = False  # 读写寄存器读取状态
        self.ro_timer_id = None  # 只读寄存器定时器ID
        self.rw_timer_id = None  # 读写寄存器定时器ID

        # 寄存器分组
        self.read_only_groups = []  # 只读寄存器分组
        self.read_write_groups = []  # 读写寄存器分组

        # 创建界面
        self.create_widgets()

        # 延迟更新串口列表，确保界面已完全加载
        self.root.after(500, self.update_serial_ports)

        # 加载默认协议
        if self.available_protocols:
            self.protocol_var.set(self.available_protocols[0])
            self.load_protocol_config()

        # 电源状态控制
        self.power_status = "unknown"  # unknown, on, off, turning_on
        self.power_detection_rules = {}  # 电源状态检测规则
        self.tcl_power_registers = {}  # TCL协议电源状态寄存器值存储

        # 添加串口工作器
        from serial_worker import SerialWorker
        self.serial_worker = SerialWorker(serial_manager, protocol_parser)
        self.serial_worker.start()

        # 居中显示主窗口
        self.center_window(self.root)

    def set_app_icon(self):
        """设置应用程序图标"""
        try:
            icon_path = self.get_resource_path("favicon.ico")
            if os.path.exists(icon_path):
                self.root.iconbitmap(icon_path)
                debug_print(f"图标已设置: {icon_path}")
            else:
                debug_print(f"警告：图标文件未找到: {icon_path}")
        except Exception as e:
            debug_print(f"图标设置失败: {e}")

    def get_resource_path(self, relative_path):
        """获取资源文件的绝对路径"""
        try:
            if hasattr(sys, '_MEIPASS'):
                # 打包后的环境
                base_path = sys._MEIPASS
            else:
                # 开发环境
                base_path = os.path.abspath(".")

            path = os.path.join(base_path, relative_path)
            debug_print(f"资源路径: {path}")
            return path
        except Exception as e:
            debug_print(f"获取资源路径失败: {e}")
            return relative_path

    def setup_chinese_font(self):
        """设置中文字体支持"""
        self.chinese_font = None
        chinese_fonts = ['Microsoft YaHei', 'SimHei', 'SimSun', 'KaiTi', 'FangSong', 'Arial Unicode MS']

        for font_name in chinese_fonts:
            try:
                test_font = tk.font.Font(family=font_name, size=10)
                self.chinese_font = font_name
                debug_print(f"使用中文字体: {font_name}")
                break
            except:
                continue

        if self.chinese_font is None:
            self.chinese_font = "TkDefaultFont"
            debug_print("警告: 未找到中文字体，使用默认字体")

    def apply_chinese_font(self, widget):
        """应用中文字体到指定控件"""
        try:
            widget.configure(font=(self.chinese_font, 10))
        except:
            pass

    def center_window(self, window):
        """将窗口居中显示"""
        window.update_idletasks()
        width = window.winfo_width()
        height = window.winfo_height()
        x = (window.winfo_screenwidth() // 2) - (width // 2)
        y = (window.winfo_screenheight() // 2) - (height // 2)
        window.geometry('{}x{}+{}+{}'.format(width, height, x, y))

    def load_available_protocols(self):
        """加载可用的协议配置文件"""
        protocols = []

        # 确定协议目录的路径
        if hasattr(sys, '_MEIPASS'):
            # 打包后的环境
            protocol_dir = os.path.join(sys._MEIPASS, "protocol_configs")
        else:
            # 兼容模式：从程序自身路径推断临时目录
            # 获取当前可执行文件的绝对路径
            if getattr(sys, 'frozen', False):
                # 打包后的可执行文件路径
                exe_path = os.path.abspath(sys.executable)
            else:
                # 开发环境（备用）
                exe_path = os.path.abspath(__file__)

            # Nuitka onefile 模式下，可执行文件通常在 /tmp/onefile_xxx/ 目录
            if "/tmp/" in exe_path and "onefile_" in exe_path:
                # 提取临时目录路径
                temp_dir = os.path.dirname(exe_path)
                protocol_dir = os.path.join(temp_dir, "protocol_configs")
            else:
                # 开发环境路径
                protocol_dir = self.protocols_dir

        # 确保协议目录存在
        if not os.path.exists(protocol_dir):
            debug_print(f"协议目录不存在: {protocol_dir}")
            # 尝试创建目录（仅开发环境）
            if not (hasattr(sys, '_MEIPASS') or ("/tmp/" in exe_path and "onefile_" in exe_path)):
                os.makedirs(protocol_dir)
                debug_print(f"创建协议目录: {protocol_dir}")
        else:
            # 加载协议文件
            for file in os.listdir(protocol_dir):
                if file.endswith(".json"):
                    protocols.append(file)
            debug_print(f"从 {protocol_dir} 找到协议文件: {protocols}")

        # 如果没有找到协议文件，使用内置默认协议
        if not protocols:
            debug_print("未找到协议文件，使用内置默认协议")
            protocols = ["tf.json", "tcl.json"]

        return protocols

    def load_protocol_config(self):
        """加载选定的协议配置文件（对齐路径逻辑+增强容错）"""
        protocol_file = self.protocol_var.get()
        if not protocol_file:
            debug_print("未选择任何协议文件，跳过加载")
            return

        try:
            # 1. 对齐第一个函数的路径逻辑：统一协议目录获取方式
            if hasattr(sys, '_MEIPASS'):
                # 打包环境：Nuitka 临时目录
                protocol_dir = os.path.join(sys._MEIPASS, "protocol_configs")
            else:
                # 兼容模式：从程序路径推断（与第一个函数逻辑完全一致）
                if getattr(sys, 'frozen', False):
                    # 打包后可执行文件路径
                    exe_path = os.path.abspath(sys.executable)
                else:
                    # 开发环境：当前文件所在路径（与第一个函数保持一致）
                    exe_path = os.path.abspath(__file__)

                # Nuitka onefile 模式特征判断
                if "/tmp/" in exe_path and "onefile_" in exe_path:
                    temp_dir = os.path.dirname(exe_path)
                    protocol_dir = os.path.join(temp_dir, "protocol_configs")
                else:
                    # 开发环境/其他打包场景：使用原路径
                    protocol_dir = self.protocols_dir

            # 拼接最终文件路径
            file_path = os.path.join(protocol_dir, protocol_file)
            debug_print(f"尝试加载协议文件: {file_path}")

            # 2. 增强前置检查：避免文件不存在/为空导致的崩溃
            if not os.path.exists(file_path):
                raise FileNotFoundError(f"文件路径不存在：{file_path}")
            if os.path.getsize(file_path) == 0:
                raise ValueError(f"协议文件为空，无法解析：{file_path}")

            # 3. 读取JSON文件：增加编码容错与格式校验
            with open(file_path, "r", encoding="utf-8", errors="replace") as f:
                try:
                    self.current_protocol = json.load(f)
                except json.JSONDecodeError as json_err:
                    raise ValueError(f"JSON格式错误：{str(json_err)}（文件：{file_path}）")

            # 4. 设备地址设置：增加类型校验（避免非整数配置）
            device_address = self.current_protocol.get("device_address", 1)
            if not isinstance(device_address, int) or device_address < 0:
                debug_print(f"设备地址「{device_address}」无效，使用默认值 1")
                device_address = 1
            self.protocol_parser.set_device_address(device_address)

            # 5. 协议信息显示：补充默认值（避免空字符串）
            protocol_name = self.current_protocol.get("name", "未知协议")
            protocol_desc = self.current_protocol.get("description", "无描述信息")
            self.protocol_name_var.set(protocol_name)
            self.protocol_desc_var.set(protocol_desc)

            # 6. 电源状态检测规则：保留原逻辑，补充变量初始化
            self.power_detection_rules = {}
            self.tcl_power_registers = {}  # 提前初始化，避免未定义报错
            if "同飞" in protocol_name:
                self.power_detection_rules = {
                    "type": "tongfei",
                    "registers": {"status_register": 0}
                }
                debug_print("同飞协议电源状态检测: 寄存器0，低字节=2且高字节≠0为开机")
            elif "TCL" in protocol_name:
                self.power_detection_rules = {
                    "type": "tcl",
                    "registers": {"work_mode": 40980, "control_mode": 40981}
                }
                debug_print("TCL协议电源状态检测: 40980∈[0,1,2]且40981∉[0,7]为开机")
            else:
                debug_print(f"未识别协议「{protocol_name}」，不配置电源状态检测")

            # 7. 重置电源状态：保留原逻辑
            self.power_status = "unknown"
            self.update_power_button_status("unknown")

            # 8. 清空寄存器表格：增加空表判断（避免无数据时循环报错）
            for table in [self.ro_table, self.rw_table]:
                if table.get_children():  # 仅当表格有数据时才删除
                    for row in table.get_children():
                        table.delete(row)
            self.register_values.clear()  # 清空旧值存储，避免数据残留

            # 9. 处理只读寄存器：补充字段默认值（应对配置缺失）
            ro_registers = self.current_protocol.get("registers", {}).get("read_only", [])
            debug_print(f"读取到 {len(ro_registers)} 个只读寄存器")
            for reg in ro_registers:
                # 为每个字段设置默认值，避免KeyError
                address = reg.get("address", "")
                name = reg.get("name", f"未命名_{address}")  # 地址为空时显示"未命名_"
                unit = reg.get("unit", "")
                desc = reg.get("description", "无描述")
                data_type = reg.get("data_type", "uint16")
                default_value = reg.get("default_value", "--")
                # 地址转字符串：避免非字符串地址导致表格插入失败
                self.ro_table.insert("", "end", values=(str(address), name, default_value, unit, desc, data_type, ""))
                self.register_values[str(address)] = default_value

            # 10. 处理读写寄存器：同只读寄存器，补充默认值
            rw_registers = self.current_protocol.get("registers", {}).get("read_write", [])
            debug_print(f"读取到 {len(rw_registers)} 个读写寄存器")
            for reg in rw_registers:
                address = reg.get("address", "")
                name = reg.get("name", f"未命名_{address}")
                unit = reg.get("unit", "")
                desc = reg.get("description", "无描述")
                data_type = reg.get("data_type", "uint16")
                default_value = reg.get("default_value", "")
                self.rw_table.insert("", "end", values=(str(address), name, default_value, unit, desc, data_type, ""))
                self.register_values[str(address)] = default_value

            # 11. 完成加载：保留原逻辑
            self.create_register_groups()
            self.status_msg.set(f"已加载协议: {protocol_name}")
            debug_print(f"协议「{protocol_name}」加载完成")

        # 12. 细化错误处理：区分不同错误类型，提示更精准
        except FileNotFoundError as fnf_err:
            err_msg = f"文件未找到：{str(fnf_err)}"
            messagebox.showerror("加载失败", err_msg)
            debug_print(f"协议加载错误: {err_msg}")
        except ValueError as val_err:
            err_msg = f"文件无效：{str(val_err)}"
            messagebox.showerror("加载失败", err_msg)
            debug_print(f"协议加载错误: {err_msg}")
        except Exception as e:
            # 捕获其他未知错误，避免程序崩溃
            err_msg = f"未知错误：{str(e)}"
            messagebox.showerror("加载失败", err_msg)
            debug_print(f"协议加载错误: {err_msg}")
    def update_power_button_status(self, status):
        """更新电源按钮状态"""
        if status == "on":
            self.power_on_btn.config(bg="#5cb85c", text="一键开机", state="normal")  # 绿色
            self.power_status = "on"
            debug_print("电源按钮状态: 开机(绿色)")
        elif status == "off":
            self.power_on_btn.config(bg="#d9534f", text="一键开机", state="normal")  # 红色
            self.power_status = "off"
            debug_print("电源按钮状态: 关机(红色)")
        elif status == "turning_on":
            self.power_on_btn.config(bg="#f0ad4e", text="开机中", state="disabled")  # 橙色
            self.power_status = "turning_on"
            debug_print("电源按钮状态: 开机中(橙色)")
        elif status == "unknown":
            self.power_on_btn.config(bg="#777", text="一键开机", state="normal")  # 灰色
            self.power_status = "unknown"
            debug_print("电源按钮状态: 未知(灰色)")

    def create_register_groups(self):
        """创建寄存器分组"""
        self.read_only_groups = []
        self.read_write_groups = []

        if not self.current_protocol:
            return

        protocol_name = self.current_protocol.get("name", "")

        # 获取只读寄存器地址
        ro_registers = self.current_protocol.get("registers", {}).get("read_only", [])
        ro_addresses = []
        for reg in ro_registers:
            address = self.convert_register_address(reg.get("address"))
            if address is not None:
                ro_addresses.append(address)

        # 获取读写寄存器地址
        rw_registers = self.current_protocol.get("registers", {}).get("read_write", [])
        rw_addresses = []
        for reg in rw_registers:
            address = self.convert_register_address(reg.get("address"))
            if address is not None:
                rw_addresses.append(address)

        # 对地址排序
        ro_addresses.sort()
        rw_addresses.sort()

        # 根据协议类型使用不同的分组策略
        if "同飞" in protocol_name:
            # 同飞协议：使用较小的分组（最多20个寄存器一组）
            self.read_only_groups = self._create_groups(ro_addresses, 20)
            self.read_write_groups = self._create_groups(rw_addresses, 20)
            debug_print(f"同飞协议分组 - 只读: {len(self.read_only_groups)}组, 读写: {len(self.read_write_groups)}组")
        else:
            # TCL协议：使用较大的分组（最多100个寄存器一组）
            self.read_only_groups = self._create_groups(ro_addresses, 100)
            self.read_write_groups = self._create_groups(rw_addresses, 100)
            debug_print(f"TCL协议分组 - 只读: {len(self.read_only_groups)}组, 读写: {len(self.read_write_groups)}组")

    def _create_groups(self, addresses, max_group_size):
        """创建寄存器分组"""
        if not addresses:
            return []

        groups = []
        current_group = []

        for addr in addresses:
            if not current_group:
                current_group.append(addr)
            else:
                # 检查是否可以加入当前组
                if len(current_group) < max_group_size and addr - current_group[0] < max_group_size:
                    current_group.append(addr)
                else:
                    # 完成当前组
                    start_addr = current_group[0]
                    count = current_group[-1] - current_group[0] + 1
                    groups.append({
                        "start": start_addr,
                        "count": count,
                        "registers": current_group.copy()
                    })
                    current_group = [addr]

        # 添加最后一个组
        if current_group:
            start_addr = current_group[0]
            count = current_group[-1] - current_group[0] + 1
            groups.append({
                "start": start_addr,
                "count": count,
                "registers": current_group.copy()
            })

        return groups

    def convert_register_address(self, address):
        """转换寄存器地址格式"""
        if isinstance(address, int):
            return address
        elif isinstance(address, str):
            # 处理十六进制字符串
            if address.startswith('0x'):
                return int(address, 16)
            else:
                # 尝试转换为整数
                try:
                    return int(address)
                except ValueError:
                    debug_print(f"无法转换地址: {address}")
                    return None
        else:
            debug_print(f"未知的地址类型: {type(address)} - {address}")
            return None

    def create_widgets(self):
        """创建界面组件"""
        # 创建主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        # 配置网格权重
        self.root.columnconfigure(0, weight=1)
        self.root.rowconfigure(0, weight=1)
        main_frame.columnconfigure(0, weight=1)
        main_frame.columnconfigure(1, weight=1)
        main_frame.rowconfigure(2, weight=1)
        main_frame.rowconfigure(3, weight=1)
        main_frame.rowconfigure(4, weight=1)

        # 顶部控制区域
        top_frame = ttk.Frame(main_frame)
        top_frame.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 10))

        # 串口配置区域
        serial_frame = ttk.LabelFrame(top_frame, text="串口配置", padding="10")
        serial_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 5))
        self.create_serial_widgets(serial_frame)

        # 协议配置区域
        protocol_frame = ttk.LabelFrame(top_frame, text="协议配置", padding="10")
        protocol_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(5, 0))
        self.create_protocol_widgets(protocol_frame)

        # 设备控制区域
        control_frame = ttk.LabelFrame(top_frame, text="设备控制", padding="10")
        control_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(5, 0))
        self.create_control_widgets(control_frame)

        # 只读寄存器区域
        ro_frame = ttk.LabelFrame(main_frame, text="只读寄存器", padding="10")
        ro_frame.grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 5))
        self.create_register_table(ro_frame, "read_only")

        # 读写寄存器区域
        rw_frame = ttk.LabelFrame(main_frame, text="读写寄存器", padding="10")
        rw_frame.grid(row=3, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(0, 5))
        self.create_register_table(rw_frame, "read_write")

        # 通信日志区域
        log_frame = ttk.LabelFrame(main_frame, text="通信日志", padding="10")
        log_frame.grid(row=4, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S))
        self.create_log_widgets(log_frame)

        # 状态栏
        status_frame = ttk.Frame(main_frame, relief=tk.SUNKEN)
        status_frame.grid(row=5, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(5, 0))

        self.status_msg = tk.StringVar(value="就绪")
        status_label = ttk.Label(status_frame, textvariable=self.status_msg, foreground="#007acc")
        status_label.pack(side=tk.LEFT, padx=10)

        # 绑定关闭事件
        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)

    def create_serial_widgets(self, parent):
        """创建串口配置组件"""
        serial_inner_frame = ttk.Frame(parent)
        serial_inner_frame.pack(fill=tk.BOTH, expand=True)

        # 第一行串口配置
        row0 = ttk.Frame(serial_inner_frame)
        row0.pack(fill=tk.X, pady=(0, 5))

        ttk.Label(row0, text="端口:").pack(side=tk.LEFT, padx=(0, 5))
        self.port_var = tk.StringVar()
        self.port_combo = ttk.Combobox(row0, textvariable=self.port_var, width=15, state="readonly")
        self.port_combo.pack(side=tk.LEFT, padx=(0, 15))

        ttk.Label(row0, text="波特率:").pack(side=tk.LEFT, padx=(0, 5))
        self.baudrate_var = tk.StringVar(value="9600")
        baudrate_combo = ttk.Combobox(row0, textvariable=self.baudrate_var,
                                      values=["9600", "19200", "38400", "57600", "115200"],
                                      width=10, state="readonly")
        baudrate_combo.pack(side=tk.LEFT, padx=(0, 15))

        self.refresh_btn = ttk.Button(row0, text="刷新端口", command=self.update_serial_ports)
        self.refresh_btn.pack(side=tk.LEFT, padx=(0, 15))

        self.connect_btn = ttk.Button(row0, text="连接", command=self.toggle_connection, width=8)
        self.connect_btn.pack(side=tk.LEFT)

        # 第二行串口配置
        row1 = ttk.Frame(serial_inner_frame)
        row1.pack(fill=tk.X)

        ttk.Label(row1, text="数据位:").pack(side=tk.LEFT, padx=(0, 5))
        self.databits_var = tk.StringVar(value="8")
        databits_combo = ttk.Combobox(row1, textvariable=self.databits_var,
                                      values=["5", "6", "7", "8"], width=5, state="readonly")
        databits_combo.pack(side=tk.LEFT, padx=(0, 15))

        ttk.Label(row1, text="停止位:").pack(side=tk.LEFT, padx=(0, 5))
        self.stopbits_var = tk.StringVar(value="1")
        stopbits_combo = ttk.Combobox(row1, textvariable=self.stopbits_var,
                                      values=["1", "1.5", "2"], width=5, state="readonly")
        stopbits_combo.pack(side=tk.LEFT, padx=(0, 15))

        ttk.Label(row1, text="校验位:").pack(side=tk.LEFT, padx=(0, 5))
        self.parity_var = tk.StringVar(value="N")
        parity_combo = ttk.Combobox(row1, textvariable=self.parity_var,
                                    values=["N", "E", "O", "S", "M"], width=5, state="readonly")
        parity_combo.pack(side=tk.LEFT)

    def create_protocol_widgets(self, parent):
        """创建协议配置组件"""
        protocol_inner_frame = ttk.Frame(parent)
        protocol_inner_frame.pack(fill=tk.BOTH, expand=True)

        # 协议选择行
        protocol_row1 = ttk.Frame(protocol_inner_frame)
        protocol_row1.pack(fill=tk.X, pady=(0, 10))

        ttk.Label(protocol_row1, text="选择协议:").pack(side=tk.LEFT, padx=(0, 5))
        self.protocol_var = tk.StringVar()
        self.protocol_combo = ttk.Combobox(protocol_row1, textvariable=self.protocol_var,
                                           values=self.available_protocols, width=20, state="readonly")
        self.protocol_combo.pack(side=tk.LEFT, padx=(0, 10))
        self.protocol_combo.bind("<<ComboboxSelected>>", lambda e: self.load_protocol_config())

        ttk.Button(protocol_row1, text="重新加载", command=self.load_protocol_config).pack(side=tk.LEFT)

        # 协议信息行
        protocol_row2 = ttk.Frame(protocol_inner_frame)
        protocol_row2.pack(fill=tk.X, pady=(0, 5))

        ttk.Label(protocol_row2, text="协议名称:").pack(side=tk.LEFT, padx=(0, 5))
        self.protocol_name_var = tk.StringVar(value="未选择协议")
        ttk.Label(protocol_row2, textvariable=self.protocol_name_var, foreground="#007acc").pack(side=tk.LEFT)

        # 协议描述行
        protocol_row3 = ttk.Frame(protocol_inner_frame)
        protocol_row3.pack(fill=tk.X)

        ttk.Label(protocol_row3, text="协议描述:").pack(side=tk.LEFT, padx=(0, 5))
        self.protocol_desc_var = tk.StringVar(value="")
        desc_label = ttk.Label(protocol_row3, textvariable=self.protocol_desc_var, wraplength=300, foreground="#555555")
        desc_label.pack(side=tk.LEFT, fill=tk.X, expand=True)

    def create_control_widgets(self, parent):
        """创建设备控制组件"""
        control_inner_frame = ttk.Frame(parent)
        control_inner_frame.pack(fill=tk.BOTH, expand=True)

        # 状态显示
        status_row = ttk.Frame(control_inner_frame)
        status_row.pack(fill=tk.X, pady=(0, 10))

        ttk.Label(status_row, text="设备状态:", font=(self.chinese_font, 10, "bold")).pack(side=tk.LEFT, padx=(0, 5))
        self.status_var = tk.StringVar(value="未连接")
        status_label = ttk.Label(status_row, textvariable=self.status_var, foreground="#d9534f")
        status_label.pack(side=tk.LEFT)

        # 控制按钮第一行
        button_row1 = ttk.Frame(control_inner_frame)
        button_row1.pack(fill=tk.X, pady=(0, 5))

        self.read_all_btn = ttk.Button(button_row1, text="读取所有寄存器", command=self.read_all_registers)
        self.read_all_btn.pack(side=tk.LEFT, padx=(0, 5))

        self.start_ro_read_btn = ttk.Button(button_row1, text="启动只读读取", command=self.toggle_ro_reading)
        self.start_ro_read_btn.pack(side=tk.LEFT, padx=(0, 5))

        self.start_rw_read_btn = ttk.Button(button_row1, text="启动读写读取", command=self.toggle_rw_reading)
        self.start_rw_read_btn.pack(side=tk.LEFT, padx=(0, 5))

        self.start_log_btn = ttk.Button(button_row1, text="开始记录", command=self.toggle_logging)
        self.start_log_btn.pack(side=tk.LEFT)

        # 控制按钮第二行
        button_row2 = ttk.Frame(control_inner_frame)
        button_row2.pack(fill=tk.X)

        # self.power_on_btn = ttk.Button(button_row2, text="一键开机", command=self.power_on)
        # self.power_on_btn.pack(side=tk.LEFT, padx=(0, 5))
        # === 替换：使用 tk.Button 替代 ttk.Button 以便设置背景色 ===
        self.power_on_btn = tk.Button(button_row2, text="一键开机", command=self.power_on,
                                      bg="#d9534f", fg="white", font=(self.chinese_font, 10, "bold"),
                                      width=10, relief="raised", bd=2)
        self.power_on_btn.pack(side=tk.LEFT, padx=(0, 5))

        self.power_off_btn = ttk.Button(button_row2, text="一键关机", command=self.power_off)
        self.power_off_btn.pack(side=tk.LEFT)

        # 初始状态：红色（关机）
        self.update_power_button_status("off")

    def create_register_table(self, parent, table_type):
        """创建寄存器表格"""
        parent.columnconfigure(0, weight=1)
        parent.rowconfigure(0, weight=1)

        # 创建表格
        columns = ("address", "name", "value", "unit", "description", "data_type", "timestamp")

        if table_type == "read_only":
            self.ro_table = ttk.Treeview(parent, columns=columns, show="headings", height=8)
            table = self.ro_table
        else:
            self.rw_table = ttk.Treeview(parent, columns=columns, show="headings", height=8)
            table = self.rw_table

        # 设置列宽和标题
        col_widths = {"address": 80, "name": 120, "value": 100, "unit": 60,
                      "description": 200, "data_type": 80, "timestamp": 150}

        table.heading("address", text="地址")
        table.heading("name", text="名称")
        table.heading("value", text="当前值")
        table.heading("unit", text="单位")
        table.heading("description", text="描述 (点击查看详情)")
        table.heading("data_type", text="数据类型")
        table.heading("timestamp", text="更新时间")

        for col, width in col_widths.items():
            table.column(col, width=width, minwidth=width)

        # 添加滚动条
        scrollbar = ttk.Scrollbar(parent, orient=tk.VERTICAL, command=table.yview)
        table.configure(yscrollcommand=scrollbar.set)

        # 布局表格和滚动条
        table.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))

        # 绑定事件
        if table_type == "read_only":
            table.bind('<ButtonRelease-1>', lambda e: self.on_table_click(e, table))
        else:
            table.bind("<Double-1>", self.on_rw_table_double_click)
            table.bind('<ButtonRelease-1>', lambda e: self.on_table_click(e, table))

        # 应用字体
        self.apply_chinese_font(table)

    def create_log_widgets(self, parent):
        """创建日志组件"""
        parent.columnconfigure(0, weight=1)
        parent.rowconfigure(0, weight=1)

        # 创建通信日志文本框和滚动条
        self.comm_log = tk.Text(parent, height=8, wrap=tk.WORD, state=tk.DISABLED, bg="#f8f9fa")
        comm_scrollbar = ttk.Scrollbar(parent, orient=tk.VERTICAL, command=self.comm_log.yview)
        self.comm_log.configure(yscrollcommand=comm_scrollbar.set)

        # 配置标签样式
        self.comm_log.tag_configure("timestamp", foreground=self.timestamp_color, font=(self.chinese_font, 9, "bold"))
        self.comm_log.tag_configure("tx", foreground=self.tx_color, font=(self.chinese_font, 9))
        self.comm_log.tag_configure("rx", foreground=self.rx_color, font=(self.chinese_font, 9))

        # 日志按钮框架
        log_button_frame = ttk.Frame(parent)
        log_button_frame.grid(row=1, column=0, columnspan=2, sticky=(tk.E), pady=(5, 0))

        # 添加按钮
        self.save_log_btn = ttk.Button(log_button_frame, text="保存日志", command=self.save_communication_log)
        self.save_log_btn.pack(side=tk.RIGHT, padx=(5, 0))

        clear_log_btn = ttk.Button(log_button_frame, text="清空日志", command=self.clear_communication_log)
        clear_log_btn.pack(side=tk.RIGHT, padx=(5, 0))

        # 布局日志区域
        self.comm_log.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
        comm_scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))

        # 应用字体
        self.apply_chinese_font(self.comm_log)

    def _check_power_status_from_register(self, address, value):
        """从寄存器值检查电源状态"""
        if not self.power_detection_rules:
            return

        protocol_type = self.power_detection_rules.get("type")
        registers = self.power_detection_rules.get("registers", {})

        if protocol_type == "tongfei":
            # 同飞协议：寄存器0 - 机组运行状态
            status_register = registers.get("status_register")
            if str(address) == str(status_register):
                # 低字节：运行状态 (0-空闲,1-准备启动,2-运行,3-延时停机,4-故障)
                # 高字节：工作模式 (0-待机,1-制冷,2-制热,3-自循环)
                low_byte = value & 0xFF  # 运行状态
                high_byte = (value >> 8) & 0xFF  # 工作模式

                debug_print(f"同飞状态寄存器: 值={value}, 低字节={low_byte}, 高字节={high_byte}")

                # 判断条件：低字节为2（运行）且高字节不等于0（非待机）
                if low_byte == 2 and high_byte != 0:
                    if self.power_status != "on":
                        self.update_power_button_status("on")
                        debug_print("同飞设备: 检测到开机状态")
                else:
                    if self.power_status != "off":
                        self.update_power_button_status("off")
                        debug_print("同飞设备: 检测到关机状态")

        elif protocol_type == "tcl":
            # TCL协议：需要同时检查40980和40981两个寄存器
            # 先存储当前读取到的值
            work_mode_reg = registers.get("work_mode")
            control_mode_reg = registers.get("control_mode")

            if str(address) == str(work_mode_reg):
                self.tcl_power_registers['work_mode'] = value
            elif str(address) == str(control_mode_reg):
                self.tcl_power_registers['control_mode'] = value

            # 当两个寄存器值都获取到时进行判断
            if 'work_mode' in self.tcl_power_registers and 'control_mode' in self.tcl_power_registers:
                work_mode = self.tcl_power_registers['work_mode']
                control_mode = self.tcl_power_registers['control_mode']

                debug_print(f"TCL电源状态: 工作模式={work_mode}, 控制模式={control_mode}")

                # 判断条件：工作模式为0,1,2 且 控制模式不等于0和7
                if work_mode in [0, 1, 2] and control_mode not in [0, 7]:
                    if self.power_status != "on":
                        self.update_power_button_status("on")
                        debug_print("TCL设备: 检测到开机状态")
                else:
                    if self.power_status != "off":
                        self.update_power_button_status("off")
                        debug_print("TCL设备: 检测到关机状态")

    # 电源控制方法
    def power_on(self):
        """一键开机"""
        if not self.is_connected:
            messagebox.showerror("错误", "请先连接设备")
            return

        if not self.current_protocol:
            messagebox.showerror("错误", "请先加载协议配置")
            return

        # === 新增：设置开机中状态 ===
        self.update_power_button_status("turning_on")

        protocol_name = self.current_protocol.get("name", "")

        try:
            if "同飞" in protocol_name:
                threading.Thread(target=self._tongfei_power_on, daemon=True).start()
            elif "TCL" in protocol_name:
                threading.Thread(target=self._tcl_power_on, daemon=True).start()
            else:
                messagebox.showinfo("提示", "当前协议不支持一键开机功能")
                # === 新增：恢复按钮状态 ===
                self.update_power_button_status("off")
        except Exception as e:
            messagebox.showerror("错误", f"开机命令发送失败: {str(e)}")
            # === 新增：恢复按钮状态 ===
            self.update_power_button_status("off")

    def _tongfei_power_on(self):
        """同飞液冷机开机流程"""
        try:
            self.send_register_value(4097, 1, "uint16")
            time.sleep(0.2)
            self.send_register_value(4099, 18, "uint16")
            time.sleep(0.2)
            self.update_status("同飞液冷机已开机，工作模式设置为制冷，温度设置为18℃")
        except Exception as e:
            self.show_error(f"同飞液冷机开机失败: {str(e)}")

    def _tcl_power_on(self):
        """TCL液冷机开机流程"""
        try:
            self.send_register_value(45079, 1, "uint16")
            time.sleep(1.5)
            self.send_register_value(45056, 1, "uint16")
            self.update_status("TCL液冷机已开机，工作模式为监控模式，监控温度为制冷")
        except Exception as e:
            self.show_error(f"TCL液冷机开机失败: {str(e)}")

    def power_off(self):
        """一键关机"""
        if not self.is_connected:
            messagebox.showerror("错误", "请先连接设备")
            return

        if not self.current_protocol:
            messagebox.showerror("错误", "请先加载协议配置")
            return

        protocol_name = self.current_protocol.get("name", "")

        try:
            if "同飞" in protocol_name:
                self.send_register_value(4097, 0, "uint16")
                # === 修改：设置关机状态 ===
                self.update_power_button_status("off")
                self.status_msg.set("同飞液冷机已关机")
            elif "TCL" in protocol_name:
                self.send_register_value(45079, 0, "uint16")
                time.sleep(0.3)
                self.send_register_value(45056, 0, "uint16")
                # === 修改：设置关机状态 ===
                self.update_power_button_status("off")
                self.status_msg.set("TCL液冷机已关机")
            else:
                messagebox.showinfo("提示", "当前协议不支持一键关机功能")
        except Exception as e:
            messagebox.showerror("错误", f"关机命令发送失败: {str(e)}")

    def update_status(self, message):
        """线程安全的UI状态更新"""
        self.root.after(0, lambda: self.status_msg.set(message))

    def show_error(self, message):
        """线程安全的错误显示"""
        self.root.after(0, lambda: messagebox.showerror("错误", message))

    # 定时读取相关方法
    def toggle_ro_reading(self):
        """切换只读寄存器读取状态"""
        if self.is_reading_ro:
            self.stop_ro_reading()
        else:
            self.start_ro_reading()

    def start_ro_reading(self):
        """启动只读寄存器定时读取（1秒间隔）"""
        if not self.is_connected:
            messagebox.showerror("错误", "请先连接设备")
            return

        self.is_reading_ro = True
        self.start_ro_read_btn.config(text="停止只读读取")
        self.status_msg.set("已启动只读寄存器定时读取（1秒间隔）")

        # 立即执行第一次读取
        self._read_read_only_registers()

    def _read_read_only_registers(self):
        """读取只读寄存器"""
        # 在开始读取前再次检查状态，确保用户没有在此期间停止读取
        if not self.is_reading_ro or not self.is_connected:
            return

        if self.read_only_groups:
            self.serial_worker.read_registers_batch(
                self.read_only_groups,
                callback=self._on_read_only_registers_read
            )
        else:
            # 如果没有只读寄存器分组，仍然安排下一次读取
            self._schedule_next_ro_read()

    def _on_read_only_registers_read(self, result):
        """只读寄存器读取完成回调"""
        # 在回调中也要检查状态，因为可能在读取过程中用户停止了读取
        if not self.is_reading_ro or not self.is_connected:
            return

        if not result['success']:
            error_msg = result.get('error', '未知错误')
            debug_print(f"只读寄存器读取失败: {error_msg}")
            # 即使失败也安排下一次读取
            self._schedule_next_ro_read()
            return

        parsed_data = result['data']
        failed_count = result.get('failed_count', 0)

        # 更新UI
        self.root.after(0, lambda: self._update_read_only_registers_ui(parsed_data, failed_count))

        # 安排下一次读取
        self._schedule_next_ro_read()

    def _schedule_next_ro_read(self):
        """安排下一次只读寄存器读取"""
        if self.is_reading_ro and self.is_connected:
            # 取消之前的定时器（如果有）
            if self.ro_timer_id:
                self.root.after_cancel(self.ro_timer_id)
            # 安排1秒后的下一次读取
            self.ro_timer_id = self.root.after(1000, self._read_read_only_registers)

    def _update_read_only_registers_ui(self, parsed_data, failed_count):
        """更新只读寄存器UI显示"""
        update_count = 0
        current_time = time.time()

        # 更新只读寄存器表格
        for item in self.ro_table.get_children():
            values = list(self.ro_table.item(item, "values"))
            if len(values) > 0:
                address = values[0]
                if str(address) in parsed_data:
                    reg_config = self.find_register_config(int(address), "read_only")
                    if reg_config:
                        raw_value = parsed_data[str(address)]
                        data_type = reg_config.get("data_type", "uint16")
                        scale = reg_config.get("scale", 1)

                        try:
                            scaled_value = self.protocol_parser.parse_register_value(raw_value, data_type, scale)

                            # 对浮点数保留3位小数
                            if isinstance(scaled_value, float):
                                display_value = round(scaled_value, 3)
                            else:
                                display_value = scaled_value

                            self.update_register_value(self.ro_table, address, display_value)
                            update_count += 1

                            # === 新增：检查电源状态寄存器 ===
                            self._check_power_status_from_register(address, raw_value)  # 使用原始值进行判断

                        except Exception as e:
                            debug_print(f"解析只读寄存器 {address} 值失败: {e}")

        # 记录数据
        if self.is_logging and parsed_data:
            self.data_logger.log_data(parsed_data)

        self.timestamps.append(current_time)

        status_msg = f"只读寄存器已更新 {update_count} 个"
        if failed_count > 0:
            status_msg += f", {failed_count} 个读取失败"

        self.status_msg.set(status_msg)

    def stop_ro_reading(self):
        """停止只读寄存器定时读取"""
        self.is_reading_ro = False
        self.start_ro_read_btn.config(text="启动只读读取")
        self.status_msg.set("已停止只读寄存器定时读取")

        # 取消定时器
        if self.ro_timer_id:
            self.root.after_cancel(self.ro_timer_id)
            self.ro_timer_id = None

    def toggle_rw_reading(self):
        """切换读写寄存器读取状态"""
        if self.is_reading_rw:
            self.stop_rw_reading()
        else:
            self.start_rw_reading()

    def start_rw_reading(self):
        """启动读写寄存器定时读取（5秒间隔）"""
        if not self.is_connected:
            messagebox.showerror("错误", "请先连接设备")
            return

        self.is_reading_rw = True
        self.start_rw_read_btn.config(text="停止读写读取")
        self.status_msg.set("已启动读写寄存器定时读取（5秒间隔）")

        # 立即执行第一次读取
        self._read_read_write_registers()

    def _read_read_write_registers(self):
        """读取读写寄存器"""
        # 在开始读取前再次检查状态，确保用户没有在此期间停止读取
        if not self.is_reading_rw or not self.is_connected:
            return

        if self.read_write_groups:
            self.serial_worker.read_registers_batch(
                self.read_write_groups,
                callback=self._on_read_write_registers_read
            )
        else:
            # 如果没有读写寄存器分组，仍然安排下一次读取
            self._schedule_next_rw_read()

    def _on_read_write_registers_read(self, result):
        """读写寄存器读取完成回调"""
        # 在回调中也要检查状态，因为可能在读取过程中用户停止了读取
        if not self.is_reading_rw or not self.is_connected:
            return

        if not result['success']:
            error_msg = result.get('error', '未知错误')
            debug_print(f"读写寄存器读取失败: {error_msg}")
            # 即使失败也安排下一次读取
            self._schedule_next_rw_read()
            return

        parsed_data = result['data']
        failed_count = result.get('failed_count', 0)

        # 更新UI
        self.root.after(0, lambda: self._update_read_write_registers_ui(parsed_data, failed_count))

        # 安排下一次读取
        self._schedule_next_rw_read()

    def _schedule_next_rw_read(self):
        """安排下一次读写寄存器读取"""
        if self.is_reading_rw and self.is_connected:
            # 取消之前的定时器（如果有）
            if self.rw_timer_id:
                self.root.after_cancel(self.rw_timer_id)
            # 安排5秒后的下一次读取
            self.rw_timer_id = self.root.after(5000, self._read_read_write_registers)

    def _update_read_write_registers_ui(self, parsed_data, failed_count):
        """更新读写寄存器UI显示"""
        update_count = 0

        # 更新读写寄存器表格
        for item in self.rw_table.get_children():
            values = list(self.rw_table.item(item, "values"))
            if len(values) > 0:
                address = values[0]
                if str(address) in parsed_data:
                    reg_config = self.find_register_config(int(address), "read_write")
                    if reg_config:
                        raw_value = parsed_data[str(address)]
                        data_type = reg_config.get("data_type", "uint16")
                        scale = reg_config.get("scale", 1)

                        try:
                            scaled_value = self.protocol_parser.parse_register_value(raw_value, data_type, scale)

                            # 对浮点数保留3位小数
                            if isinstance(scaled_value, float):
                                display_value = round(scaled_value, 3)
                            else:
                                display_value = scaled_value

                            self.update_register_value(self.rw_table, address, display_value)
                            update_count += 1
                        except Exception as e:
                            debug_print(f"解析读写寄存器 {address} 值失败: {e}")

        status_msg = f"读写寄存器已更新 {update_count} 个"
        if failed_count > 0:
            status_msg += f", {failed_count} 个读取失败"

        self.status_msg.set(status_msg)

    def stop_rw_reading(self):
        """停止读写寄存器定时读取"""
        self.is_reading_rw = False
        self.start_rw_read_btn.config(text="启动读写读取")
        self.status_msg.set("已停止读写寄存器定时读取")

        # 取消定时器
        if self.rw_timer_id:
            self.root.after_cancel(self.rw_timer_id)
            self.rw_timer_id = None

    # 表格事件处理方法
    def on_table_click(self, event, table):
        """处理表格点击事件，用于显示描述详情"""
        region = table.identify_region(event.x, event.y)

        if region == "cell":
            column = table.identify_column(event.x)

            if column == "#5":  # 描述列
                row = table.identify_row(event.y)
                item = table.item(row)
                values = item['values']
                description = values[4] if len(values) > 4 else ""

                if description:
                    self.show_description_dialog(description)

    def on_rw_table_double_click(self, event):
        """处理读写寄存器表格的双击事件"""
        selection = self.rw_table.selection()
        if not selection:
            return

        item = selection[0]
        values = self.rw_table.item(item, "values")
        if len(values) < 6:
            return

        address = values[0]
        name = values[1]
        current_value = values[2]
        unit = values[3]
        data_type = values[5]

        # 获取寄存器的配置信息
        reg_config = None
        if self.current_protocol:
            for reg in self.current_protocol.get("registers", {}).get("read_write", []):
                if str(reg.get("address", "")) == str(address):
                    reg_config = reg
                    break

        self.create_setting_dialog(address, name, current_value, unit, data_type, reg_config)

    # 日志相关方法
    def log_communication(self, direction, data):
        """记录通信数据到日志"""
        timestamp = datetime.now().strftime("%H:%M:%S.%f")[:-3]
        hex_data = self.format_bytes(data)

        if direction == "Tx":
            direction_text = "发送"
            tag_name = "tx"
        else:
            direction_text = "接收"
            tag_name = "rx"

        # 更新日志显示
        self.comm_log.configure(state=tk.NORMAL)
        self.comm_log.insert(tk.END, timestamp + " ", "timestamp")
        self.comm_log.insert(tk.END, f"{direction_text}: {hex_data}\n", tag_name)
        self.comm_log.see(tk.END)
        self.comm_log.configure(state=tk.DISABLED)

    def clear_communication_log(self):
        """清空通信日志"""
        self.comm_log.configure(state=tk.NORMAL)
        self.comm_log.delete(1.0, tk.END)
        self.comm_log.configure(state=tk.DISABLED)

    def save_communication_log(self):
        """将通信日志保存到文件"""
        self.comm_log.configure(state=tk.NORMAL)
        log_content = self.comm_log.get(1.0, tk.END).strip()
        self.comm_log.configure(state=tk.DISABLED)

        if not log_content:
            messagebox.showinfo("提示", "日志为空，无需保存。")
            return

        file_path = filedialog.asksaveasfilename(
            title="保存通信日志",
            defaultextension=".txt",
            filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
        )

        if not file_path:
            return

        try:
            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(log_content)
            self.status_msg.set(f"日志已保存至: {file_path}")
            messagebox.showinfo("成功", f"日志已成功保存到\n{file_path}")
        except Exception as e:
            messagebox.showerror("保存失败", f"无法保存日志文件:\n{str(e)}")

    # 对话框方法
    def show_description_dialog(self, description):
        """显示描述详情的对话框"""
        dialog = tk.Toplevel(self.root)
        dialog.title("描述详情")
        dialog.geometry("600x400")
        dialog.transient(self.root)
        dialog.grab_set()

        text_frame = ttk.Frame(dialog, padding="10")
        text_frame.pack(fill=tk.BOTH, expand=True)

        text_widget = tk.Text(text_frame, wrap=tk.WORD, font=(self.chinese_font, 10))
        text_widget.insert(tk.END, description)
        text_widget.config(state=tk.DISABLED)

        scrollbar = ttk.Scrollbar(text_frame, orient=tk.VERTICAL, command=text_widget.yview)
        text_widget.configure(yscrollcommand=scrollbar.set)

        text_widget.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        button_frame = ttk.Frame(dialog)
        button_frame.pack(pady=10)

        ttk.Button(button_frame, text="关闭", command=dialog.destroy).pack()
        self.center_window(dialog)

    def create_setting_dialog(self, address, name, current_value, unit, data_type, reg_config):
        """创建设置寄存器值的对话框"""
        dialog = tk.Toplevel(self.root)
        dialog.title(f"设置 {name}")
        dialog.geometry("400x200")
        dialog.transient(self.root)
        dialog.grab_set()

        ttk.Label(dialog, text=f"寄存器: {name} ({address})").pack(pady=10)

        value_frame = ttk.Frame(dialog)
        value_frame.pack(pady=10)

        ttk.Label(value_frame, text="值:").grid(row=0, column=0, padx=5)
        value_var = tk.StringVar(value=current_value)
        value_entry = ttk.Entry(value_frame, textvariable=value_var, width=15)
        value_entry.grid(row=0, column=1, padx=5)

        if unit:
            ttk.Label(value_frame, text=unit).grid(row=0, column=2, padx=5)

        # 添加范围提示
        if reg_config:
            min_val = reg_config.get("min_value")
            max_val = reg_config.get("max_value")
            if min_val is not None and max_val is not None:
                ttk.Label(dialog, text=f"取值范围: {min_val} - {max_val}").pack(pady=5)

        button_frame = ttk.Frame(dialog)
        button_frame.pack(pady=20)

        def set_value():
            try:
                new_value = value_var.get()
                # 验证数值范围
                if reg_config:
                    min_val = reg_config.get("min_value")
                    max_val = reg_config.get("max_value")
                    if min_val is not None and max_val is not None:
                        num_value = float(new_value)
                        if num_value < min_val or num_value > max_val:
                            messagebox.showerror("错误", f"值必须在 {min_val} 到 {max_val} 之间")
                            return

                # 更新表格显示
                for item in self.rw_table.get_children():
                    if self.rw_table.item(item, "values")[0] == address:
                        self.rw_table.set(item, "value", new_value)
                        break

                # 更新内部存储
                self.register_values[str(address)] = new_value

                # 发送到设备
                if self.is_connected:
                    self.send_register_value(address, new_value, data_type)

                dialog.destroy()

            except ValueError:
                messagebox.showerror("错误", "请输入有效的数值")

        ttk.Button(button_frame, text="设置", command=set_value).pack(side=tk.LEFT, padx=10)
        ttk.Button(button_frame, text="取消", command=dialog.destroy).pack(side=tk.LEFT, padx=10)

        self.center_window(dialog)

    # 核心功能方法
    @staticmethod
    def format_bytes(data):
        """将 bytes 或 bytearray 格式化为十六进制字符串"""
        if isinstance(data, (bytes, bytearray)):
            return ' '.join(f'{byte:02X}' for byte in data)
        return str(data)

    def send_register_value(self, address, value, data_type):
        """发送寄存器值到设备"""
        try:
            # 使用统一的地址转换方法
            if isinstance(address, str):
                address = self.convert_register_address(address)

            # 查找寄存器配置以获取缩放因子
            reg_config = None
            if self.current_protocol:
                for reg_type in ['read_write', 'read_only']:
                    for reg in self.current_protocol.get("registers", {}).get(reg_type, []):
                        reg_address = self.convert_register_address(reg.get("address"))
                        if reg_address == address:
                            reg_config = reg
                            break
                    if reg_config:
                        break

            scale = reg_config.get("scale", 1) if reg_config else 1

            # 根据数据类型转换值并进行单位换算
            raw_value = 0
            if data_type == "float":
                num_value = float(value) / scale
                raw_value = int(round(num_value))
            elif data_type == "uint16":
                raw_value = int(float(value) / scale)
            elif data_type == "int16":
                # 处理有符号16位整数
                num_value = float(value) / scale
                # 确保在int16范围内
                if num_value < -32768 or num_value > 32767:
                    raise ValueError(f"值 {num_value} 超出int16范围 [-32768, 32767]")
                # 转换为无符号表示
                if num_value < 0:
                    raw_value = int(65536 + num_value)
                else:
                    raw_value = int(num_value)
            else:
                raw_value = int(float(value) / scale)

            debug_print(
                f"发送寄存器值: 地址={address}(0x{address:04X}), 显示值={value}, 原始值={raw_value}, 数据类型={data_type}, 缩放因子={scale}")

            # 异步发送写入命令
            return self.serial_worker.send_command(
                "WRITE_REGISTER",
                address,
                raw_value,
                callback=lambda result: self._on_register_write(result, address)
            )

        except ValueError as e:
            messagebox.showerror("错误", f"无效的数值格式: {str(e)}")
            return False
        except Exception as e:
            messagebox.showerror("错误", f"发送命令失败: {str(e)}")
            return False

    def _on_register_write(self, result, address):
        """寄存器写入完成回调"""
        if result['success']:
            self.root.after(0, lambda: self.status_msg.set(f"寄存器 {address} 写入成功"))
        else:
            error_msg = result.get('error', '未知错误')
            self.root.after(0, lambda: messagebox.showerror("错误", f"写入寄存器失败: {error_msg}"))

    def read_all_registers(self):
        """读取所有寄存器的值"""
        if not self.is_connected:
            return False

        try:
            if not self.current_protocol:
                return False

            # 合并所有寄存器分组
            all_groups = self.read_only_groups + self.read_write_groups

            if not all_groups:
                return False

            debug_print(f"总共需要读取 {len(all_groups)} 个寄存器分组")

            # 发送批量读取命令
            return self.serial_worker.read_registers_batch(
                all_groups,
                callback=self._on_all_registers_read
            )

        except Exception as e:
            debug_print(f"读取所有寄存器错误: {str(e)}")
            import traceback
            debug_print(traceback.format_exc())
            return False

    def _on_all_registers_read(self, result):
        """所有寄存器读取完成回调"""
        if not result['success']:
            error_msg = result.get('error', '未知错误')
            self.root.after(0, lambda: self.status_msg.set(f"寄存器读取失败: {error_msg}"))
            return

        parsed_data = result['data']
        failed_count = result.get('failed_count', 0)

        debug_print(f"所有寄存器读取完成: 成功 {len(parsed_data)} 个, 失败 {failed_count} 个")

        # 更新UI
        self.root.after(0, lambda: self._update_all_registers_ui(parsed_data, failed_count))

    def _update_all_registers_ui(self, parsed_data, failed_count):
        """更新所有寄存器UI显示"""
        update_count = 0
        current_time = time.time()

        # 更新只读寄存器表格
        for item in self.ro_table.get_children():
            values = list(self.ro_table.item(item, "values"))
            if len(values) > 0:
                address = values[0]
                if str(address) in parsed_data:
                    reg_config = self.find_register_config(int(address), "read_only")
                    if reg_config:
                        raw_value = parsed_data[str(address)]
                        data_type = reg_config.get("data_type", "uint16")
                        scale = reg_config.get("scale", 1)

                        try:
                            scaled_value = self.protocol_parser.parse_register_value(raw_value, data_type, scale)

                            # 对浮点数保留3位小数
                            if isinstance(scaled_value, float):
                                display_value = round(scaled_value, 3)
                            else:
                                display_value = scaled_value

                            self.update_register_value(self.ro_table, address, display_value)
                            update_count += 1
                        except Exception as e:
                            debug_print(f"解析只读寄存器 {address} 值失败: {e}")

        # 更新读写寄存器表格
        for item in self.rw_table.get_children():
            values = list(self.rw_table.item(item, "values"))
            if len(values) > 0:
                address = values[0]
                if str(address) in parsed_data:
                    reg_config = self.find_register_config(int(address), "read_write")
                    if reg_config:
                        raw_value = parsed_data[str(address)]
                        data_type = reg_config.get("data_type", "uint16")
                        scale = reg_config.get("scale", 1)

                        try:
                            scaled_value = self.protocol_parser.parse_register_value(raw_value, data_type, scale)

                            # 对浮点数保留3位小数
                            if isinstance(scaled_value, float):
                                display_value = round(scaled_value, 3)
                            else:
                                display_value = scaled_value

                            self.update_register_value(self.rw_table, address, display_value)
                            update_count += 1
                        except Exception as e:
                            debug_print(f"解析读写寄存器 {address} 值失败: {e}")

        # 记录数据
        if self.is_logging and parsed_data:
            self.data_logger.log_data(parsed_data)

        self.timestamps.append(current_time)

        status_msg = f"已更新 {update_count} 个寄存器"
        if failed_count > 0:
            status_msg += f", {failed_count} 个读取失败"

        self.status_msg.set(status_msg)

    def find_register_config(self, address, register_type):
        """查找寄存器配置"""
        if not self.current_protocol:
            return None

        registers = self.current_protocol.get("registers", {}).get(register_type, [])
        for reg in registers:
            reg_address = self.convert_register_address(reg.get("address"))
            if reg_address == address:
                return reg
        return None

    # 基础功能方法
    def update_serial_ports(self):
        """更新可用串口列表"""
        ports = self.serial_manager.get_available_ports()
        self.port_combo['values'] = ports
        if ports and self.port_var.get() not in ports:
            self.port_var.set(ports[0])

    def update_register_value(self, table, address, value):
        """更新寄存器值并添加时间戳"""
        timestamp = datetime.now().strftime("%Y.%m.%d %H:%M:%S.%f")[:-3]

        for item in table.get_children():
            current_values = list(table.item(item, "values"))
            if len(current_values) > 0 and str(current_values[0]) == str(address):
                current_values[2] = value
                current_values[6] = timestamp
                table.item(item, values=tuple(current_values))
                break

        self.register_values[str(address)] = value

    def toggle_connection(self):
        """切换连接状态"""
        if self.is_connected:
            self.disconnect()
        else:
            self.connect()

    def connect(self):
        """连接串口"""
        port = self.port_var.get()
        if not port:
            messagebox.showerror("错误", "请选择串口")
            return

        baudrate = int(self.baudrate_var.get())
        bytesize = int(self.databits_var.get())
        stopbits = float(self.stopbits_var.get())
        parity = self.parity_var.get()

        try:
            self.serial_manager.connect(port, baudrate, bytesize, stopbits, parity)
            self.is_connected = True
            self.connect_btn.config(text="断开")
            self.status_var.set("已连接")
            self.status_msg.set(f"已连接到 {port}")

            # === 新增：连接后设置为未知状态，等待读取 ===
            self.update_power_button_status("unknown")
            self.save_config()

        except Exception as e:
            messagebox.showerror("连接错误", f"无法连接串口: {str(e)}")

    def disconnect(self):
        """断开串口连接"""
        # 停止定时读取
        if self.is_reading_ro:
            self.stop_ro_reading()
        if self.is_reading_rw:
            self.stop_rw_reading()

        self.serial_manager.disconnect()
        self.is_connected = False
        self.connect_btn.config(text="连接")
        self.status_var.set("未连接")
        self.status_msg.set("已断开连接")
        # === 新增：断开连接后设置为未知状态 ===
        self.update_power_button_status("unknown")

    def toggle_logging(self):
        """切换数据记录状态"""
        if self.is_logging:
            self.stop_logging()
        else:
            self.start_logging()

    def start_logging(self):
        """开始记录数据"""
        filename = filedialog.asksaveasfilename(
            defaultextension=".csv",
            filetypes=[("CSV files", "*.csv"), ("All files", "*.*")]
        )
        if filename:
            self.data_logger.start_logging(filename)
            self.is_logging = True
            self.start_log_btn.config(text="停止记录")
            self.status_msg.set(f"数据记录已开始: {filename}")

    def stop_logging(self):
        """停止记录数据"""
        self.data_logger.stop_logging()
        self.is_logging = False
        self.start_log_btn.config(text="开始记录")
        self.status_msg.set("数据记录已停止")

    def save_config(self):
        """保存配置"""
        config = {
            'serial': {
                'port': self.port_var.get(),
                'baudrate': self.baudrate_var.get(),
                'databits': self.databits_var.get(),
                'stopbits': self.stopbits_var.get(),
                'parity': self.parity_var.get()
            },
            'protocol': {
                'current': self.protocol_var.get()
            }
        }
        self.config_manager.save_config(config)

    def on_closing(self):
        """程序关闭时的处理"""
        if self.is_connected:
            self.disconnect()
        if self.is_logging:
            self.stop_logging()
        if self.is_reading_ro:
            self.stop_ro_reading()
        if self.is_reading_rw:
            self.stop_rw_reading()

        # 停止串口工作器
        self.serial_worker.stop()

        self.root.destroy()